package com.allenkerr.lucene;

import org.apache.commons.io.FileUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.*;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.lionsoul.jcseg.analyzer.JcsegAnalyzer;
import org.lionsoul.jcseg.tokenizer.core.JcsegTaskConfig;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * Created by 小柯(Allen Kerr) on 17.4.29 12:19.
 */
public class IndexManager {
    //资源文件所在目录
    private static String searchSourceDirPath;
    //索引文件所在目录
    private static String searchIndexDirPath;

    //使用静态代码块加载配置文件，初始化属性
    static {
        InputStream inputStream = IndexManager.class.getClassLoader().getResourceAsStream("config.properties");
        Properties properties = new Properties();
        try {
            properties.load(inputStream);
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("读取配置文件失败！");
        }
        searchSourceDirPath = properties.getProperty("search_source_dir");
        searchIndexDirPath = properties.getProperty("search_index_dir");
    }

    /*public static void main(String[] args) {
        System.out.println(searchSourceDirPath);
        System.out.println(searchIndexDirPath);
    }*/

    /**
     * 索引创建，简易版
     */
    public static void createIndex1() throws IOException {
        System.out.println("IndexManager.createIndex1 --> 简易版");
        //1. 创建文档列表，保存多个Document对象
        List<Document> docList = new ArrayList<>();

        //2. 指定资源文件所在的目录
        File sourceDir = new File(searchSourceDirPath);

        //3. 循环资源文件夹取出文件
        for (File file : sourceDir.listFiles()) {
            //文件名
            String fileName = file.getName();
            //文件内容
            String fileContext = FileUtils.readFileToString(file);
            //文件大小
            Long fileSize = FileUtils.sizeOf(file);

            //4. 使用TextField对象存值，构造函数(域名, 域值, 是否存储yes/no);
            TextField nameField = new TextField("fileName", fileName, Field.Store.YES);
            TextField contextField = new TextField("fileContext", fileContext, Field.Store.YES);
            TextField sizeField = new TextField("fileSize", fileSize.toString(), Field.Store.YES);

            //5.1 创建文档对象(一个文件对应一个文档对象)
            Document doc = new Document();
            //5.2 将所有的域都存入文档对象中
            doc.add(nameField);
            doc.add(contextField);
            doc.add(sizeField);
            //5.3 将文档对象存储文档列表中
            docList.add(doc);
        }
        //6. 创建分词器，标准分词器StandardAnalyzer：支持英文，中文是单字分词
        StandardAnalyzer analyzer = new StandardAnalyzer();

        //7. 指定索引和文档存储的目录
        /**
         * 参考：http://stackoverflow.com/questions/35925482/lucene-using-fsdirectory
         */
//        File indexDir = new File(searchIndexDirPath);
//        Directory directory = FSDirectory.open(indexDir.toPath());
        Directory directory = FSDirectory.open(Paths.get(searchIndexDirPath));

        //8. 创建写对象的初始化对象
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);

        //9. 创建索引和文档写对象
        IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);

        //10. 将文档加入到索引和文档的写对象中
        int i = 0;
        for (Document doc : docList) {
            indexWriter.addDocument(doc);
            i++;
        }
        System.out.println("共添加 " + i + " 条索引。");
        //11. 提交
        indexWriter.commit();
        //12. 关闭流
        indexWriter.close();
    }

    /**
     * 索引创建，优化版
     * 使用Jcseg中文分词器
     */
    public static void createIndex2() throws IOException {
        System.out.println("IndexManager.createIndex2 --> 优化版");
        //1. 创建文档列表，保存多个Document对象
        List<Document> docList = new ArrayList<>();

        //2. 指定资源文件所在的目录
        File sourceDir = new File(searchSourceDirPath);

        //3. 循环资源文件夹取出文件
        for (File file : sourceDir.listFiles()) {
            //文件名
            String fileName = file.getName();
            //文件内容
            String fileContext = FileUtils.readFileToString(file);
            //文件大小
            Long fileSize = FileUtils.sizeOf(file);

            //4. 使用TextField对象存值，构造函数(域名, 域值, 是否存储yes/no);
            /*TextField nameField = new TextField("fileName", fileName, Field.Store.YES);
            TextField contextField = new TextField("fileContext", fileContext, Field.Store.YES);
            TextField sizeField = new TextField("fileSize", fileSize.toString(), Field.Store.YES);*/

            //是否分词：yes，文件名需要提供搜索，且文件名可以分词
            //是否索引：yes，文件名需要索引，提供搜索
            //是否存储：yes，需要提供搜索结果的显示
            TextField nameField = new TextField("fileName", fileName, Field.Store.YES);
            // 是否分词：yes，文件内容需要提供搜索，且文件内容可以分词
            //是否索引：yes，文件内容需要索引，提供搜索
            //是否存储：yes or no，视搜索结果是否需要展示文件内容来定
            TextField contextField = new TextField("fileContext", fileContext, Field.Store.NO);
            // 是否分词：yes，文档大小需要使用lucene的算法进行分词，方便比较大小
            //是否索引：yes，文档大小也是搜索条件之一
            //是否存储：yes，需要在搜索结果里展示文档大小
//            LegacyLongField sizeField = new LegacyLongField("fileSize", fileSize, Field.Store.YES);
            /**
             * 在Lucene 6.0中，IntField替换为IntPoint，FloatField替换为FloatPoint，LongField替换为LongPoint，
             * DoubleField替换为DoublePoint。
             * IntPoint
             * 对int型字段索引，只索引不存储，提供了一些静态工厂方法用于创建一般的查询，提供了不同于文本的数值类型存储方式，
             * 使用KD-trees索引
             * http://codepub.cn/2016/05/20/Lucene-6-0-in-action-2-All-kinds-of-Field-and-sort-operations/
             */
            LongPoint sizeField = new LongPoint("fileSize", fileSize);
            //如果需要存储，需创建独立的StoredField域，并在doc中添加
            //参考：http://stackoverflow.com/questions/42659616/lucene-6-adding-intfields
            StoredField fileSize4Store = new StoredField("fileSize", fileSize);

            //5.1 创建文档对象(一个文件对应一个文档对象)
            Document doc = new Document();
            //5.2 将所有的域都存入文档对象中
            doc.add(nameField);
            doc.add(contextField);
            doc.add(sizeField);
            //添加存储域
            doc.add(fileSize4Store);
            //5.3 将文档对象存储文档列表中
            docList.add(doc);
        }
        //6. 创建分词器，标准分词器StandardAnalyzer：支持英文，中文是单字分词
//        StandardAnalyzer analyzer = new StandardAnalyzer();
        //lucene 4.x版本
        //Analyzer analyzer = new JcsegAnalyzer4X(JcsegTaskConfig.COMPLEX_MODE);
        //lucene 6.3.0以及以上版本
        Analyzer analyzer = new JcsegAnalyzer(JcsegTaskConfig.COMPLEX_MODE);

        //7. 指定索引和文档存储的目录
        /**
         * 参考：http://stackoverflow.com/questions/35925482/lucene-using-fsdirectory
         */
//        File indexDir = new File(searchIndexDirPath);
//        Directory directory = FSDirectory.open(indexDir.toPath());
        Directory directory = FSDirectory.open(Paths.get(searchIndexDirPath));

        //8. 创建写对象的初始化对象
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);

        //9. 创建索引和文档写对象
        IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);

        //10. 将文档加入到索引和文档的写对象中
        int i = 0;
        for (Document doc : docList) {
            indexWriter.addDocument(doc);
            i++;
        }
        System.out.println("共添加 " + i + " 条索引。");
        //11. 提交
        indexWriter.commit();
        //12. 关闭流
        indexWriter.close();
    }

    /**
     * 索引删除
     */
    public static void deleteIndex() throws IOException {
        //1. 创建分词器，StandardAnalyzer：良好支持英文，中文只能单字分词
//        StandardAnalyzer analyzer = new StandardAnalyzer();
        //lucene 4.x版本
        //Analyzer analyzer = new JcsegAnalyzer4X(JcsegTaskConfig.COMPLEX_MODE);
        //lucene 6.3.0以及以上版本
        Analyzer analyzer = new JcsegAnalyzer(JcsegTaskConfig.COMPLEX_MODE);
        //2. 指定索引和文档的目录
        FSDirectory directory = FSDirectory.open(Paths.get(searchIndexDirPath));
        //3. 创建写对象的初始化对象
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
        //4. 创建索引和文档写对象
        IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);

        //5. 删除索引
        //5.1 删除所有
        indexWriter.deleteAll();
        //5.2 根据名称删除
        //Term词元，就是一个词，构造函数(域名, 关键字);
//        indexWriter.deleteDocuments(new Term("fileName", "spring"));
//        indexWriter.deleteDocuments(new Term("fileContext", "spring"));
        //6. 提交
        indexWriter.commit();
        //7. 关闭
        indexWriter.close();
    }

    /**
     * 索引更新，原理：先删除，再添加
     */
    public static void updateIndex(String field, String keyword) throws IOException {
        //1. 创建分词器，StandardAnalyzer：良好支持英文，中文只能单字分词
//        StandardAnalyzer analyzer = new StandardAnalyzer();
        //lucene 4.x版本
        //Analyzer analyzer = new JcsegAnalyzer4X(JcsegTaskConfig.COMPLEX_MODE);
        //lucene 6.3.0以及以上版本
        Analyzer analyzer = new JcsegAnalyzer(JcsegTaskConfig.COMPLEX_MODE);
        //2. 指定索引和文档的目录
        FSDirectory directory = FSDirectory.open(Paths.get(searchIndexDirPath));
        //3. 创建写对象的初始化对象
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
        //4. 创建索引和文档写对象
        IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);

        //5.1 要删除的对象 --> 根据名称找
        //Term词元，就是一个词，构造函数(域名, 关键字);
        Term term = new Term(field, keyword);
        //5.2 要添加的对象
        Document doc = new Document();
        doc.add(new TextField("fileName", "test", Field.Store.YES));
        doc.add(new TextField("fileContext", "this is a test file.", Field.Store.NO));
//        Field fileSize = new NumericDocValuesField("fileSize", 1024L);
        Field fileSize = new LegacyLongField("fileSize", 1024L, Field.Store.YES);
        doc.add(fileSize);
        //5.3 更新
        indexWriter.updateDocument(term, doc);

        //6. 提交
        indexWriter.commit();
        //7. 关闭
        indexWriter.close();
    }

}
